<script lang="ts">
    import { untrack } from "svelte";
    import { useClassList } from "../utils/useClassList";
    import type { TeleportBoxItem, TeleportBoxProps } from "./TeleportBox";
    import Icon from "../Common/Icons/Icon.svelte";
    import { Input } from "../Input";
    import { Button } from "../Button";
    import { Checkbox } from "../Checkbox";
    import { VirtualList } from "../VirtualList";
    import { Text } from "../Typography";
    import { _ } from "svelte-i18n";
    import { useFormItemContext } from "../FormItem/FormItem.svelte";

    let { value = $bindable([]), ...props }: TeleportBoxProps = $props();
    const ctx = useFormItemContext();

    const store = $state({
        leftList: [] as TeleportBoxItem[],
        rightList: [] as TeleportBoxItem[],
        originList: [] as TeleportBoxItem[],
        flatList: [] as TeleportBoxItem[],
        dataMap: {},
        checkedAll: false,
    });

    const classList = $derived(
        useClassList(props, "cm-teleport-box", {
            "cm-teleport-box--disabled": props.disabled,
        })
    );

    const onFilter = (v: any) => {
        if (v) {
            const list = store.flatList?.filter((item) => {
                if (props.filter) {
                    return props.filter(item, v);
                }
                if (item.children?.length) {
                    const childs = item.children?.filter((sub) => (item.label as string).includes(v));
                    return childs?.length;
                }
                return (item.label as string).includes(v);
            });
            store.leftList = list || [];
        } else {
            store.leftList = store.flatList || [];
        }
    };

    const initData = () => {
        const map: { [key: string]: TeleportBoxItem } = {};
        let originList: TeleportBoxItem[] = [];
        const data = props.data;
        untrack(() => {
            const vals = value;
            if (props.defaultValue) {
                props.defaultValue.forEach((v) => {
                    if (!vals.includes(v)) {
                        vals.push(v);
                    }
                });
            }
            const list = data?.flatMap((item) => {
                if (item.children?.length) {
                    originList = originList.concat(item.children);
                } else {
                    originList.push(item);
                }
                return [item, ...(item.children ? item.children : [])].flat();
            });
            store.originList = originList;
            store.flatList = list || [];
            store.flatList?.forEach((item) => {
                if (vals.includes(item.value)) {
                    item.checked = true;
                }
                map[item.value] = item;
            });
            store.dataMap = map;
            store.leftList = store.flatList;

            value = [...vals];
        });
    };

    initData();

    $effect(() => {
        initData();
    });

    $effect(() => {
        const val = value;
        const total = store.originList.filter((item) => !item.disabled || (item.disabled && item.checked)).length;
        if (props.data?.length && val.length === total) {
            store.checkedAll = true;
        } else {
            store.checkedAll = false;
        }
        const arr = val.map((v) => {
            return (store.dataMap as any)[v];
        });
        store.rightList = arr;
    });

    const onChecked = (checked: boolean, item: TeleportBoxItem) => {
        if (props.disabled || item.disabled) {
            return;
        }
        item.checked = checked;
        let val: any[] = value || [];
        const v = item.value;
        if (checked) {
            if (!val.includes(v)) {
                val = val.concat(v);
            }
        } else {
            const index = val.indexOf(v);
            if (index > -1) {
                val.splice(index, 1);
            }
        }
        value = [...val];
        props.onchange?.($state.snapshot(value));
        ctx?.onFieldChange?.();
    };

    const onRemoveItem = (item: TeleportBoxItem) => {
        if (item.disabled) {
            return;
        }
        item.checked = false;
        const val = value;
        const index = val.indexOf(item.value);
        if (index > -1) {
            val.splice(index, 1);
        }
        value = [...val];
        props.onchange?.([...val]);
        ctx?.onFieldChange?.();
    };

    const clearAll = () => {
        const vals = value;
        const restVals: any[] = [];
        vals.forEach((val) => {
            const item = (store.dataMap as any)[val];
            if (item.disabled) {
                restVals.push(item.value);
                return;
            }
            item.checked = false;
        });
        value = restVals;
        props.onchange?.(restVals);
        ctx?.onFieldChange?.();
    };

    const selectAll = () => {
        const vals: any[] = [];
        for (const val in store.dataMap) {
            const item = (store.dataMap as any)[val];
            if (item.children?.length) {
                continue;
            }
            if (item.disabled) {
                if (item.checked) {
                    vals.push(item.value);
                }
                continue;
            }
            item.checked = true;
            vals.push(item.value);
        }
        value = vals;
        props.onchange?.(vals);
        ctx?.onFieldChange?.();
    };

    const onItemChecked = (item: TeleportBoxItem, checked: boolean) => {
        onChecked(checked, item);
    };
    const onItemRemove = (item: TeleportBoxItem) => {
        onRemoveItem(item);
    };
</script>

{#snippet itemRender(item: TeleportBoxItem, index: number, itemProps: any)}
    {#if item.children?.length}
        <div {...itemProps} class="cm-teleport-group-title">{item.label}</div>
    {:else}
        <div {...itemProps}>
            {#if props.renderSourceItem}
                {@render props.renderSourceItem(item, onItemChecked.bind(null, item))}
            {:else}
                <Checkbox disabled={item.disabled} checked={item.checked} onchange={(v) => onChecked?.(v, item)} label={item.label} />
            {/if}
        </div>
    {/if}
{/snippet}

{#snippet targetItemRender(item: TeleportBoxItem, index: number, itemProps: any)}
    {#if item}
        <div {...itemProps}>
            {#if props.renderSelectedItem}
                {@render props.renderSelectedItem(item, onItemRemove.bind(null, item))}
            {:else}
                <div class="cm-teleport-right-item">
                    <Text>{@render renderLabel(item)}</Text>
                    {#if !item.disabled}
                        <Icon icon="times" class="cm-teleport-right-item-close" onclick={() => onRemoveItem?.(item)} />
                    {/if}
                </div>
            {/if}
        </div>
    {/if}
{/snippet}

{#snippet renderLabel(item: TeleportBoxItem)}
    {#if typeof item.label === "function"}
        {@render item.label()}
    {:else}
        {item.label}
    {/if}
{/snippet}

<div class={classList} style={props.style}>
    <section class="cm-teleport-left">
        <div class="cm-teleport-header">
            <span class="cm-teleport-header-total">{$_("TeleportBox.total")}：{store.originList?.length}</span>
            {#if store.checkedAll}
                <Button size="small" theme="borderless" class="cm-teleport-select-all" onclick={clearAll}>{$_("TeleportBox.uncheckAll")}</Button>
            {:else}
                <Button size="small" theme="borderless" class="cm-teleport-select-all" onclick={selectAll}>{$_("TeleportBox.checkAll")}</Button>
            {/if}
        </div>
        <div class="cm-teleport-filter">
            <Input onchange={onFilter} placeholder={$_("TeleportBox.search")} clearable>
                {#snippet suffix()}
                    <Icon icon="search" />
                {/snippet}
            </Input>
        </div>
        <div class="cm-teleport-left-list">
            {#if props.virtual}
                <VirtualList items={store.leftList} itemEstimatedSize={30} {itemRender} />
            {:else}
                {#each store.leftList as item (item.value)}
                    {#if item.children?.length}
                        <div class="cm-teleport-group-title">{@render renderLabel(item)}</div>
                    {:else if props.renderSourceItem}
                        {@render props.renderSourceItem(item, onItemChecked.bind(null, item))}
                    {:else}
                        <Checkbox disabled={item.disabled} checked={item.checked} onchange={(v) => onChecked(v, item)} label={item.label} />
                    {/if}
                {/each}
            {/if}
        </div>
    </section>
    <section class="cm-teleport-right">
        <div class="cm-teleport-header cm-teleport-right-header">
            <span class="cm-teleport-header-total">{$_("TeleportBox.selectedNum")}：{value.length}</span>
            {#if value.length}
                <Button size="small" theme="borderless" class="cm-teleport-clear" onclick={clearAll}>{$_("TeleportBox.clear")}</Button>
            {/if}
        </div>
        <div class="cm-teleport-right-list">
            {#if props.virtual}
                <VirtualList items={store.rightList} itemEstimatedSize={30} itemRender={targetItemRender} />
            {:else}
                {#each value as val (val)}
                    {@const item: TeleportBoxItem = (store.dataMap as any)[val]}
                    {#if item}
                        {#if props.renderSelectedItem}
                            {@render props.renderSelectedItem(item, onItemRemove.bind(null, item))}
                        {:else}
                            <div class="cm-teleport-right-item">
                                <Text>{@render renderLabel(item)}</Text>
                                {#if !item.disabled}
                                    <Icon icon="times" class="cm-teleport-right-item-close" onclick={() => onRemoveItem(item)} />
                                {/if}
                            </div>
                        {/if}
                    {/if}
                {/each}
            {/if}
        </div>
    </section>
</div>
